package com.sugree.jibjib;

import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;

import java.io.File;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.Math;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDiskIOException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.provider.SearchRecentSuggestions;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;

import com.sugree.jibjib.data.JibJibDatabase;
import com.sugree.jibjib.data.FriendTimeline;
/*
import com.sugree.jibjib.data.PagedCursorAdapter;
import com.sugree.jibjib.data.SearchableCursorAdapter;
import com.sugree.jibjib.data.TimelineSearchSuggestionProvider;
import com.sugree.jibjib.data.TweetBinder;
import com.sugree.jibjib.data.JibJibDatabase.Tweets;
import com.sugree.jibjib.data.JibJibDatabase.Users;
*/
import com.sugree.jibjib.net.Connection;
import com.sugree.jibjib.net.ConnectionAuthenticationException;
import com.sugree.jibjib.net.ConnectionException;
import com.sugree.jibjib.net.ConnectionUnavailableException;

import com.sugree.util.Shortener;
import com.sugree.util.Bitly;
import com.sugree.util.Twitgoo;
import com.sugree.util.Yfrog;
import com.sugree.util.PublicService;

/**
 * @author sugree
 * 
 */
public class SendTweet extends Activity implements LocationListener {

	public static final String TAG = "JibJib";

	public static final String URL_REGEXP = "\\b(([\\w-]+://?|www[.])[^\\s<>]+(?:\\([\\w\\d]+\\)|([^[:punct:]\\s]|/)))";

	// Handler message codes
	public static final int MSG_UPDATE_STATUS = 1;
	public static final int MSG_AUTHENTICATION_ERROR = 2;
	public static final int MSG_LOCATION_UNAVAILABLE_ERROR = 3;
	public static final int MSG_SERVICE_UNAVAILABLE_ERROR = 4;
	public static final int MSG_CONNECTION_TIMEOUT_EXCEPTION = 5;
	public static final int MSG_CONNECTION_EXCEPTION = 6;

	// Dialog identifier codes
	public static final int DIALOG_AUTHENTICATION_FAILED = 1;
	public static final int DIALOG_OBTAINING_LOCATION = 2;
	public static final int DIALOG_SENDING_MESSAGE = 3;
	public static final int DIALOG_LOCATION_UNAVAILABLE = 4;
	public static final int DIALOG_SERVICE_UNAVAILABLE = 5;
	public static final int DIALOG_CONNECTION_TIMEOUT = 6;
	public static final int DIALOG_SHORTENING_URL = 7;
	public static final int DIALOG_RESIZING_PICTURE = 8;

	public static final String BUNDLE_KEY_REPLY_ID = "reply_id";
	public static final String BUNDLE_KEY_URI = "uri";
	public static final String BUNDLE_KEY_TEXT = "text";

	public static final int REQUEST_PICTURE = 1;
	public static final int REQUEST_CAMERA = 2;

	// Views and widgets
	private Button mUrlButton;
	private Button mPictureButton;
	private Button mCameraButton;
	private Button mSendButton;
	private EditText mEditText;
	private TextView mCharsLeftText;
	private CheckBox mCheckLocation;
	private ImageView mImage;

	// Text limits
	private int mCurrentChars = 0;
	private int mLimitChars = 140;
	private boolean mInitializing = false;

	protected LocationManager mLM;
	protected SharedPreferences mSP;
	protected ProgressDialog mProgressDialog;

	protected Location mLocation = null;
	protected Bitmap mBitmap = null;
	protected Uri mUri = null;
	protected Uri mCameraUri = null;
	protected long mReplyId = 0;
	protected long mCurrentId = 0;
	protected Exception mException = null;

	/**
	 * Called when the activity is first created.
	 * 
	 * @see android.app.Activity#onCreate(android.os.Bundle)
	 * 
	 * @param savedInstanceState
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
		mSP = PreferenceManager.getDefaultSharedPreferences(this);
		mLM = (LocationManager)getSystemService(LOCATION_SERVICE);  

		if (savedInstanceState != null) {
			if (savedInstanceState.containsKey(BUNDLE_KEY_REPLY_ID)) {
				mReplyId = savedInstanceState.getLong(BUNDLE_KEY_REPLY_ID);
			}
		}
		Log.d(TAG, "onCreate() "+mReplyId);
		
		loadTheme();
		setContentView(R.layout.sendtweet);

		// Set up views
		mUrlButton = (Button) findViewById(R.id.urlButton);
		mCameraButton = (Button) findViewById(R.id.cameraButton);
		mPictureButton = (Button) findViewById(R.id.pictureButton);
		mSendButton = (Button) findViewById(R.id.messageEditSendButton);
		mEditText = (EditText) findViewById(R.id.edtTweetInput);
		mCharsLeftText = (TextView) findViewById(R.id.messageEditCharsLeftTextView);
		mCheckLocation = (CheckBox) findViewById(R.id.chkLocation);
		mImage = (ImageView) findViewById(R.id.imageThumbnail);

		initUI();
	}

	@Override
	public void onNewIntent(final Intent newIntent) {
		super.onNewIntent(newIntent);
		Log.d(TAG, "onNewIntent() " + newIntent.getAction());
	}

	@Override
	protected void onStart() {
		super.onStart();
		final Intent intent = getIntent();
		Log.d(TAG, "onStart() " + intent.getAction());
		if (Intent.ACTION_SEND.equals(intent.getAction())) {
			final Bundle extras = intent.getExtras();
			final Bundle appData = intent.getBundleExtra("com.sugree.jibjib.app_data");

			String message = mEditText.getText().toString();
			if (message.equals("")) {
				message = (String) extras.getString(Intent.EXTRA_SUBJECT);
				if (message != null) {
					mEditText.setText(message);
				}
				message = (String) extras.getString(Intent.EXTRA_TEXT);
				if (message != null) {
					mEditText.setText(message);
				}
			}

			if (appData != null && appData.containsKey("reply_id")) {
				mReplyId = appData.getLong("reply_id");
			}

			Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
			if (uri != null) {
				mUri = uri;
				try {
					mBitmap = rescaleBitmap(mUri);
					mImage.setImageBitmap(mBitmap);

					/*
					InputStream in = getContentResolver().openInputStream(uri);
					Bitmap bitmap = BitmapFactory.decodeStream(in);
					Log.d(TAG, uri.toString()+" "+bitmap.getWidth()+"x"+bitmap.getHeight());
					*/
				} catch (NullPointerException e) {
					Log.e(TAG, "rescaleBitmap() "+uri.toString(), e);
				} catch (FileNotFoundException e) {
					Log.e(TAG, uri.toString(), e);
				}
			}
		}
		Log.d(TAG, "onStart "+mReplyId);
	}

	@Override
	protected void onResume() {
		super.onResume();
		Log.d(TAG, "onResume()");
		mCharsLeftText.setText(String.valueOf(mLimitChars - mEditText.length()));
	}

	@Override
	protected void onStop() {
		super.onStop();
		Log.d(TAG, "onStop()");
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		Log.d(TAG, "onDestroy()");
	}

	@Override
	protected void onSaveInstanceState(Bundle state) {
		Log.d(TAG, "onSaveInstanceState()");
		state.putLong(BUNDLE_KEY_REPLY_ID, mReplyId);
		if (mUri != null) {
			state.putString(BUNDLE_KEY_URI, mUri.toString());
		}
		state.putString(BUNDLE_KEY_TEXT, mEditText.getText().toString());
		super.onSaveInstanceState(state);
	}

	@Override
	protected void onRestoreInstanceState(Bundle state) {
		super.onRestoreInstanceState(state);
		Log.d(TAG, "onRestoreInstanceState()");
		mReplyId = state.getLong(BUNDLE_KEY_REPLY_ID);
		String uri = state.getString(BUNDLE_KEY_URI);
		if (uri != null) {
			mUri = Uri.parse(uri);
			try {
				mBitmap = rescaleBitmap(mUri);
				mImage.setImageBitmap(mBitmap);
			} catch (NullPointerException e) {
				Log.e(TAG, "rescaleBitmap() "+uri.toString(), e);
			} catch (FileNotFoundException e) {
				Log.e(TAG, uri.toString(), e);
			}
		}
		String text = state.getString(BUNDLE_KEY_TEXT);
		if (text != null) {
			mEditText.setText(text);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);

		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.send, menu);

		Intent intent = new Intent(null, getIntent().getData());
		intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
		menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0, new ComponentName(this, TweetListActivity.class), null, intent, 0, null);

		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.preferences_menu_id:
			startActivity(new Intent(this, PreferencesActivity.class));
			break;
		}
		return super.onOptionsItemSelected(item);
	}

	/**
	 * Send the message.
	 */
	void sendMessage() {
		String msg = mEditText.getText().toString();
		if (TextUtils.isEmpty(msg.trim())) {
			Toast.makeText(SendTweet.this, R.string.cannot_send_empty_message, Toast.LENGTH_SHORT).show();
		} else {
			if (mCheckLocation.isChecked()) {
				//# FIXME
				if (mLocation == null) {
					showDialog(DIALOG_OBTAINING_LOCATION);
					mLocation = getCurrentLocation(null);
					return;
				}
				Log.d(TAG, mLocation.getLatitude()+","+mLocation.getLongitude());
			}

			showDialog(DIALOG_SENDING_MESSAGE);
			Thread thread = new Thread(mSendUpdate);
			thread.start();
			closeSoftKeyboard();
		}
	}

	void sendEmail(String toAddr, Location location) {
		Intent intent = new Intent(Intent.ACTION_SEND);
		intent.setType(getContentResolver().getType(mUri));
		intent.putExtra(Intent.EXTRA_EMAIL, new String[] { toAddr });
		intent.putExtra(Intent.EXTRA_SUBJECT, mEditText.getText().toString());
		String body = "tags: jibjib";
		if (location != null) {
			body += " geotagged"+
			" geo=lat:"+Location.convert(location.getLatitude(), Location.FORMAT_DEGREES)+
			" geo=lon:"+Location.convert(location.getLongitude(), Location.FORMAT_DEGREES);
		}
		intent.putExtra(Intent.EXTRA_TEXT, body);
		intent.putExtra(Intent.EXTRA_STREAM, mUri);
		startActivity(Intent.createChooser(intent, getText(R.string.send_email)));
	}

	private void doCamera() {
		Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

		/*
	    File f = new File(Environment.getExternalStorageDirectory()+File.pathSeparator+"jibjib",
				"jibjib-" + String.valueOf(System.currentTimeMillis()) + ".jpg");
		f.mkdir();
		*/
	    File f = new File(Environment.getExternalStorageDirectory(),
				"jibjib_tmp.jpg");
	    mCameraUri = Uri.fromFile(f);
	    intent.putExtra(MediaStore.EXTRA_OUTPUT, mCameraUri);
		/*
	    intent.putExtra(MediaStore.EXTRA_OUTPUT, MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString());
		*/

		startActivityForResult(intent, REQUEST_CAMERA);
	}

	private void doPicture() {
		//invokeMediaScanner();

		Intent intent = new Intent();
		intent.setType("image/*");
		intent.setAction(Intent.ACTION_GET_CONTENT);
		startActivityForResult(Intent.createChooser(intent, getText(R.string.get_picture)), REQUEST_PICTURE);
	}

	private void doUrl() {
		Pattern p = Pattern.compile(URL_REGEXP);
		Matcher m = p.matcher(mEditText.getText().toString());
		final ArrayList<String> urlList = new ArrayList<String>();
		String s;
		while (m.find()) {
			s = m.group();

			if (!PublicService.isImageService(s)) {
				urlList.add(s);
			}
		}
		Log.d(TAG, urlList.toString());
		if (!urlList.isEmpty()) {
			Connection aConn = new Connection(mSP);
			new UrlShortenTask(aConn).execute(urlList);
		} else {
			Toast.makeText(this, R.string.no_url_to_shorten, Toast.LENGTH_SHORT).show();
		}
	}

	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (resultCode != RESULT_OK) {
			return;
		}
		switch (requestCode) {
		case REQUEST_CAMERA:
			try {
	    		File f = new File(Environment.getExternalStorageDirectory(),
						"jibjib_tmp.jpg");
				//mBitmap = BitmapFactory.decodeFile(f.getAbsolutePath());
				/*
				mBitmap = (Bitmap)data.getExtras().get("data");
				*/
				String uri = MediaStore.Images.Media.insertImage(getContentResolver(), f.getAbsolutePath(), null, null);
				//invokeMediaScanner();
				f.delete();

				mUri = Uri.parse(uri);
				new PictureResizeTask().execute(mUri);
				/*
				mBitmap = rescaleBitmap(mUri);
				((ImageView)findViewById(R.id.imageThumbnail)).setImageBitmap(mBitmap);
				*/
			} catch (FileNotFoundException e) {
				Log.e(TAG, e.toString());
			} catch (NullPointerException e) {
				Log.e(TAG, e.toString());
			}
			break;

		case REQUEST_PICTURE:
			mUri = data.getData();
			new PictureResizeTask().execute(mUri);
			/*
			try {
				mBitmap = rescaleBitmap(mUri);
				((ImageView)findViewById(R.id.imageThumbnail)).setImageBitmap(mBitmap);
			} catch (NullPointerException e) {
				Log.e(TAG, "rescaleBitmap() "+mUri.toString(), e);
			} catch (FileNotFoundException e) {
				Log.e(TAG, mUri.toString(), e);
			}
			*/
			break;
		}
		Log.d(TAG, "activityresult "+mUri);
	}

	private void invokeMediaScanner() {
		sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+Environment.getExternalStorageDirectory())));
	}

	/**
	 * Close the on-screen keyboard.
	 */
	private void closeSoftKeyboard() {
		InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
		inputMethodManager.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
	}

	protected Bitmap rescaleBitmap(Uri uri) throws FileNotFoundException {
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
		int origWidth = options.outWidth;
		int origHeight = options.outHeight;

		int source = Math.max(origWidth, origHeight);
		int target = Integer.valueOf(mSP.getString("picture_size", "640"));
		if (target == 0) {
			target = source;
		}

		int sampleScale = 1;
		while (source/(sampleScale<<1) > target) {
			sampleScale <<= 1;
		}
		//int sampleScale = (int)Math.pow(2d, Math.floor(Math.log((float)source/target)/Math.log(2d)));
		float scale = (float)target/((float)source/sampleScale);

		Log.d(TAG, "orig "+origWidth+","+origHeight);
		Log.d(TAG, "scale "+sampleScale+" "+scale);

		/*
		Cursor c = managedQuery(uri, new String[] {MediaStore.Images.Media.DATA}, null, null, null);
		int ci = c.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
		c.moveToFirst();
		String path = c.getString(ci);
		Log.d(TAG, "path "+path);
		*/

		options.inJustDecodeBounds = false;
		options.inSampleSize = sampleScale;
		/*
		Bitmap orig = BitmapFactory.decodeStream(
			getContentResolver().openInputStream(uri),
			null,
			options);
		Bitmap orig = BitmapFactory.decodeFile(path, options);
		*/
		ParcelFileDescriptor fd = getContentResolver().openFileDescriptor(uri, "r");
		Bitmap orig = BitmapFactory.decodeFileDescriptor(
			fd.getFileDescriptor(),
			null,
			options);
		if (orig == null) {
			Log.e(TAG, "bitmap decode failed");
			throw new NullPointerException();
		}
		if (target == source) {
			return orig;
		}

		Matrix matrix = new Matrix();
		matrix.postScale(scale, scale);

		Bitmap scaled = Bitmap.createBitmap(orig, 0, 0, orig.getWidth(), orig.getHeight(), matrix, true);
		Log.d(TAG, "result "+scaled.getWidth()+","+scaled.getHeight());
		if (scaled != orig) {
			orig.recycle();
		}
		return scaled;
	}

	protected InputStream compressBitmap() throws FileNotFoundException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		mBitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);
		ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

		return bis;
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case DIALOG_AUTHENTICATION_FAILED:
			return new AlertDialog.Builder(this)
				.setIcon(android.R.drawable.ic_dialog_alert)
				.setTitle(R.string.dialog_title_authentication_failed)
				.setMessage(R.string.dialog_summary_authentication_failed)
				.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface Dialog, int whichButton) {
						startActivity(new Intent(SendTweet.this, PreferencesActivity.class));
					}
				}).create();

		case DIALOG_SERVICE_UNAVAILABLE:
			return new AlertDialog.Builder(this)
				.setIcon(android.R.drawable.ic_dialog_alert)
				.setTitle(R.string.dialog_title_service_unavailable)
				.setMessage(getString(R.string.dialog_summary_service_unavailable, new Object[] {mException.getMessage()}))
				.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface Dialog, int whichButton) {
					}
				}).create();

		case DIALOG_LOCATION_UNAVAILABLE:
			return new AlertDialog.Builder(this)
				.setIcon(android.R.drawable.ic_dialog_alert)
				.setTitle(R.string.dialog_title_location_unavailable)
				.setMessage(R.string.dialog_summary_location_unavailable)
				.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface Dialog, int whichButton) {
					}
				}).create();

		case DIALOG_OBTAINING_LOCATION:
			mProgressDialog = new ProgressDialog(this);
			mProgressDialog.setIcon(android.R.drawable.ic_dialog_info);
			mProgressDialog.setTitle(R.string.dialog_title_obtaining_location);
			mProgressDialog.setMessage(getText(R.string.dialog_summary_obtaining_location));
			return mProgressDialog;

		case DIALOG_SENDING_MESSAGE:
			mProgressDialog = new ProgressDialog(this);
			mProgressDialog.setIcon(android.R.drawable.ic_dialog_info);
			mProgressDialog.setTitle(R.string.dialog_title_sending_message);
			mProgressDialog.setMessage(getText(R.string.dialog_summary_sending_message));
			return mProgressDialog;

		case DIALOG_SHORTENING_URL:
			mProgressDialog = new ProgressDialog(this);
			mProgressDialog.setIcon(android.R.drawable.ic_dialog_info);
			mProgressDialog.setTitle(R.string.dialog_title_shortening_url);
			mProgressDialog.setMessage(getText(R.string.dialog_summary_shortening_url));
			return mProgressDialog;

		case DIALOG_RESIZING_PICTURE:
			mProgressDialog = new ProgressDialog(this);
			mProgressDialog.setIcon(android.R.drawable.ic_dialog_info);
			mProgressDialog.setTitle(R.string.dialog_title_resizing_picture);
			mProgressDialog.setMessage(getText(R.string.dialog_summary_resizing_picture));
			return mProgressDialog;

		case DIALOG_CONNECTION_TIMEOUT:
			return new AlertDialog.Builder(this)
				.setIcon(android.R.drawable.ic_dialog_alert)
				.setTitle(R.string.dialog_title_connection_timeout)
				.setMessage(R.string.dialog_summary_connection_timeout)
				.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface Dialog, int whichButton) {
					}
				}).create();

		default:
			return super.onCreateDialog(id);
		}
	}

	public void loadTheme() {
		boolean light = mSP.getBoolean("appearance_light_theme", false);
		StringBuilder theme = new StringBuilder();
		String name = mSP.getString("theme", "JibJib");
		if (name.indexOf("Theme.") > -1) {
			name = name.substring(name.indexOf("Theme."));
		}
		theme.append("Theme.");
		if (light) {
			theme.append("Light.");
		}
		theme.append(name);
		setTheme((int) getResources().getIdentifier(theme.toString(), "style", "com.sugree.jibjib"));
	}

	/**
	 * Initialize UI
	 */
	protected void initUI() {
		mSendButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				sendMessage();
			}
		});

		mCameraButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				doCamera();
			}
		});

		mPictureButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				doPicture();
			}
		});

		mUrlButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				doUrl();
			}
		});

		mEditText.addTextChangedListener(new TextWatcher() {
			public void afterTextChanged(Editable s) {
				mCurrentChars = s.length();
				mCharsLeftText.setText(String.valueOf(mLimitChars - mCurrentChars));
			}

			public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
			public void onTextChanged(CharSequence s, int start, int before, int count) {}
		});

		mEditText.setOnKeyListener(new View.OnKeyListener() {
			public boolean onKey(View v, int keyCode, KeyEvent event) {
				if (event.getAction() == KeyEvent.ACTION_DOWN) {
					mCurrentChars = mEditText.length();
					if (mCurrentChars == 0) {
						mReplyId = 0;
					}
					switch (keyCode) {
					case KeyEvent.KEYCODE_DPAD_CENTER:
						sendMessage();
						return true;
					case KeyEvent.KEYCODE_ENTER:
						if (event.isAltPressed()) {
							mEditText.append("\n");
							return true;
						}
					default:
						if (keyCode != KeyEvent.KEYCODE_DEL && mCurrentChars > mLimitChars) {
							return true;
						}
						mCharsLeftText.setText(String.valueOf(mLimitChars - mCurrentChars));
						break;
					}
				}
				return false;
			}
		});

		mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
			public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
				if (event != null) {
					if (event.isAltPressed()) {
						return false;
					}
					if (event.getAction() == event.ACTION_DOWN) {
						sendMessage();
						return true;
					}
				}
				return false;
			}
		});
	}

	protected Location getCurrentLocation(String provider) {
		if (provider == null) {
			provider = LocationManager.NETWORK_PROVIDER;
		}
		mLM.requestLocationUpdates(provider, 0, 0, this);  
		return mLM.getLastKnownLocation(provider);  
	}

	/**
	 * Message handler for messages from threads.
	 */
	protected Handler mHandler = new Handler() {
		/**
		 * Message handler
		 * 
		 * @param msg
		 */
		@Override
		public void handleMessage(Message msg) {
			JSONObject result = null;
			String username = mSP.getString("twitter_username", null);
			String password = mSP.getString("twitter_password", null);
			switch (msg.what) {
			case MSG_UPDATE_STATUS:
				result = (JSONObject) msg.obj;
				if (result.optString("error").length() > 0) {
					Toast.makeText(SendTweet.this, (CharSequence) result.optString("error"), Toast.LENGTH_LONG).show();
				} else {
					/*
					FriendTimeline fl = new FriendTimeline(getContentResolver(), username, password, mSP.getLong("last_friends_id", 0));
					try {
						fl.insertFromJSONObject(result, JibJibDatabase.Tweets.TWEET_TYPE_TWEET, true);
					} catch (JSONException e) {
						Toast.makeText(SendTweet.this, e.getMessage(), Toast.LENGTH_SHORT).show();
					}
					*/
					Toast.makeText(SendTweet.this, R.string.message_sent, Toast.LENGTH_SHORT).show();
					mReplyId = 0;
					mEditText.setText("");
				}
				try {
					dismissDialog(DIALOG_SENDING_MESSAGE);
				} catch (IllegalArgumentException e) {
					Log.d(TAG, e.getMessage());
				}
				finish();
				break;

			case MSG_AUTHENTICATION_ERROR:
				switch (msg.arg1) {
				case MSG_UPDATE_STATUS:
					try {
						dismissDialog(DIALOG_SENDING_MESSAGE);
					} catch (IllegalArgumentException e) {
						Log.d(TAG, e.getMessage());
					}
					break;
				}
				showDialog(DIALOG_AUTHENTICATION_FAILED);
				break;

			case MSG_LOCATION_UNAVAILABLE_ERROR:
				switch (msg.arg1) {
				case MSG_UPDATE_STATUS:
					try {
						dismissDialog(DIALOG_OBTAINING_LOCATION);
					} catch (IllegalArgumentException e) {
						Log.d(TAG, e.getMessage());
					}
					break;
				}
				showDialog(DIALOG_LOCATION_UNAVAILABLE);
				break;

			case MSG_SERVICE_UNAVAILABLE_ERROR:
				switch (msg.arg1) {
				case MSG_UPDATE_STATUS:
					try {
						dismissDialog(DIALOG_SENDING_MESSAGE);
					} catch (IllegalArgumentException e) {
						Log.d(TAG, e.getMessage());
					}
					break;
				}
				mException = (Exception)msg.obj;
				showDialog(DIALOG_SERVICE_UNAVAILABLE);
				break;

			case MSG_CONNECTION_TIMEOUT_EXCEPTION:
				switch (msg.arg1) {
				case MSG_UPDATE_STATUS:
					try {
						dismissDialog(DIALOG_SENDING_MESSAGE);
					} catch (IllegalArgumentException e) {
						Log.d(TAG, e.getMessage());
					}
					break;
				}
				showDialog(DIALOG_CONNECTION_TIMEOUT);
				break;

			default:
				super.handleMessage(msg);
			}
		}
	};

	public void onLocationChanged(Location location) {
		mLocation = location;
		dismissDialog(DIALOG_OBTAINING_LOCATION);
		mLM.removeUpdates(this);
		sendMessage();
	}

	public void onProviderDisabled(String provider) {
		mLM.removeUpdates(this);
		mHandler.sendMessage(mHandler.obtainMessage(MSG_LOCATION_UNAVAILABLE_ERROR, MSG_UPDATE_STATUS, 0));
	}

	public void onProviderEnabled(String provider) {
	}

	public void onStatusChanged(String provider, int status, Bundle extras) {
	}

	/**
	 * Handles threaded sending of messages.
	 */
	protected Runnable mSendUpdate = new Runnable() {
		public void run() {
			String username = mSP.getString("twitter_username", null);
			String password = mSP.getString("twitter_password", null);
			String message = mEditText.getText().toString();

			/*
			com.sugree.jibjib.net.Connection aConn = new com.sugree.jibjib.net.Connection(username, password);
			aConn.setTwitterGateway(mSP.getString("twitter_gateway", null));
			aConn.setPictureGateway(pictureGateway);
			*/
			String pictureGateway = mSP.getString("picture_gateway", null);
			com.sugree.jibjib.net.Connection aConn = new com.sugree.jibjib.net.Connection(mSP);
			JSONObject result = new JSONObject();
			try {
				Location location = null;
				if (mCheckLocation.isChecked()) {
					location = mLocation;
				}
				if (mUri != null) {
					//String mimeType = getContentResolver().getType(mUri);
					InputStream is = compressBitmap();
					String mimeType = "image/jpeg";
					String filename = "jibjib.jpg";
					String url = "";

					if (pictureGateway.startsWith("mailto:")) {
						String emailGateway = mSP.getString("email_gateway", null);
						sendEmail(emailGateway, location);
					} else if (pictureGateway.equals("http://tweetphotoapi.com") ||
							pictureGateway.equals("http://api.plixi.com")) {
						result = aConn.postTweetPhoto(is, mimeType, filename, message.trim(), location);
						if (result == null) {
							throw new ConnectionException("timeout during read");
						}
						url = result.getString("MediaUrl");
						result = aConn.updateStatus(message.trim()+" "+url, mReplyId, location);
					} else {
						result = aConn.postTwitPic(is, mimeType, filename, message.trim());
						if (result == null) {
							throw new ConnectionException("timeout during read");
						}
						url = result.optString("mediaurl", null);
						if (url == null) {
							url = result.getString("url");
						}
						result = aConn.updateStatus(message.trim()+" "+url, mReplyId, location);
					}
				} else {
					result = aConn.updateStatus(message.trim(), mReplyId, location);
				}
			} catch (UnsupportedEncodingException e) {
				Log.e(TAG, "aConn Unsupportd Encoding Exception: " + e.getMessage());
				mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_UNAVAILABLE_ERROR, MSG_UPDATE_STATUS, 0, e));
				return;
			} catch (FileNotFoundException e) {
				Log.e(TAG, e.getMessage());
				mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_UNAVAILABLE_ERROR, MSG_UPDATE_STATUS, 0, e));
				return;
			} catch (JSONException e) {
				Log.e(TAG, "mSendUpdate Response Exception: " + e.getMessage());
				mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_UNAVAILABLE_ERROR, MSG_UPDATE_STATUS, 0, e));
				return;
			} catch (ConnectionException e) {
				Log.e(TAG, "mSendUpdate Connection Exception: " + e.getMessage());
				mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_UNAVAILABLE_ERROR, MSG_UPDATE_STATUS, 0, e));
				return;
			} catch (ConnectionAuthenticationException e) {
				mHandler.sendMessage(mHandler.obtainMessage(MSG_AUTHENTICATION_ERROR, MSG_UPDATE_STATUS, 0));
				return;
			} catch (ConnectionUnavailableException e) {
				mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_UNAVAILABLE_ERROR, MSG_UPDATE_STATUS, 0, e));
				return;
			} catch (SocketTimeoutException e) {
				mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECTION_TIMEOUT_EXCEPTION, MSG_UPDATE_STATUS, 0));
				return;
			} catch (Exception e) {
				mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_UNAVAILABLE_ERROR, MSG_UPDATE_STATUS, 0, e));
				return;
			}
			mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_STATUS, result));
		}
	};

	protected class UrlShortenTask extends AsyncTask<ArrayList<String>, String, ArrayList<String> > {
		private Connection mConnection;
		private ArrayList<String> mSource;

		public UrlShortenTask(Connection con) {
			mConnection = con;
		}

		protected void onPreExecute() {
			showDialog(DIALOG_SHORTENING_URL);
			closeSoftKeyboard();
		}

		protected ArrayList<String> doInBackground(ArrayList<String>... args) {
			String login = PrivateData.BITLY_LOGIN;
			String apikey = PrivateData.BITLY_API_KEY;
			String x_login = mSP.getString("bitly_xlogin", null);
			String x_apikey = mSP.getString("bitly_xapikey", null);

			mSource = args[0];

			Twitgoo twitgoo = new Twitgoo(mConnection);
			Yfrog yfrog = new Yfrog(mConnection);
			Bitly bitly = new Bitly(login, apikey, x_login, x_apikey);
			ArrayList<String> ret = new ArrayList<String>();
			String ss;
			for(String s : args[0]) {
				boolean isImage = 
						s.toLowerCase().indexOf("gif") >= 0 ||
						s.toLowerCase().indexOf("png") >= 0 ||
						s.toLowerCase().indexOf("jpeg") >= 0 ||
						s.toLowerCase().indexOf("jpg") >= 0;
				boolean mayImage = 
						s.toLowerCase().indexOf("image") >= 0 ||
						s.toLowerCase().indexOf("img") >= 0 ||
						s.toLowerCase().indexOf("pic") >= 0;
				ss = null;

				try {
					if (ss == null && (isImage || mayImage)) {
						ss = yfrog.shorten(s);
					}
				} catch (Shortener.ApiException e) {
					Log.e(TAG, "yfrog "+e.toString());
					publishProgress("yfrog "+e.toString());
				}

				try {
					if (ss == null && (isImage || mayImage)) {
						ss = twitgoo.shorten(s);
					}
				} catch (Shortener.ApiException e) {
					Log.e(TAG, "twitgoo "+e.toString());
					publishProgress("twitgoo "+e.toString());
					if (isImage) {
						ss = s;
					}
				}

				try {
					if (ss == null) {
						ss = bitly.shorten(s);
					}
				} catch (Shortener.ApiException e) {
					Log.e(TAG, "bitly "+e.toString());
					ss = s;
				}

				publishProgress(ss);
				ret.add(ss);
			}
			Log.d(TAG, ret.toString());
			return ret;
		}

		protected void onProgressUpdate(String... progress) {
			Toast.makeText(SendTweet.this, progress[0], Toast.LENGTH_SHORT).show();
		}

		protected void onPostExecute(ArrayList<String> result) {
			String text = mEditText.getText().toString();
			for(int i=0; i<mSource.size(); i++) {
				text = text.replace(mSource.get(i), result.get(i));
			}
			mEditText.setText(text);
			dismissDialog(DIALOG_SHORTENING_URL);
		}
	}

	protected class PictureResizeTask extends AsyncTask<Uri, String, Bitmap> {

		protected void onPreExecute() {
			showDialog(DIALOG_RESIZING_PICTURE);
			closeSoftKeyboard();
		}

		protected Bitmap doInBackground(Uri... args) {
			Uri uri = args[0];
			Bitmap bitmap = null;
			try {
				bitmap = rescaleBitmap(uri);
			} catch (NullPointerException e) {
				Log.e(TAG, "rescaleBitmap() "+uri.toString(), e);
			} catch (FileNotFoundException e) {
				Log.e(TAG, uri.toString(), e);
			}
			return bitmap;
		}

		protected void onPostExecute(Bitmap result) {
			mBitmap = result;
			mImage.setImageBitmap(mBitmap);
			dismissDialog(DIALOG_RESIZING_PICTURE);
		}
	}
}
